#ifndef __M_HELPER_H__
#define __M_HELPER_H__
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#include <cerrno>
#include <sys/stat.h>
#include <sqlite3.h>
#include "logger.hpp"

namespace yqx
{
    /*
        实现一个SqliteHelper类，提供简单的sqlite数据库操作接口，完成数据的基础增删改查操作：
        1.创建/打开数据库文件
        2.对打开的数据库执行操作：
            1.表的操作
            2.数据的操作
        3.关闭数据库
    */
    class SqliteHelper
    {
    public:
        typedef int (*sqliteCallback)(void *, int, char **, char **);

        SqliteHelper(const std::string &dbfile) : _dbfile(dbfile), _handler(nullptr) {}

        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                LOG("创建/打开sqlite数据库失败:%s\n", sqlite3_errmsg(_handler))
                return false;
            }
            return true;
        }

        bool exec(const std::string &sql, sqliteCallback cb, void *arg)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                LOG("执行语句失败:%s\n", sqlite3_errmsg(_handler))
                return false;
            }
            return true;
        }

        void close()
        {
            if (_handler)
                sqlite3_close(_handler);
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };
    /*
        实现一个字符串分割类，根据sep(".")来分割字符串，把结果存到res里
    */
    class StrHelper
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &res)
        {
            size_t pos = 0, idx = 0;
            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    res.push_back(str.substr(idx));
                    return res.size();
                }
                else if (pos == idx)
                {
                    idx = pos + sep.size();
                }
                else
                {
                    res.push_back(str.substr(idx, pos - idx));
                    idx = pos + sep.size();
                }
            }
            return res.size();
        }
    };
    /*
        生成一个16进制32位的uuid，格式为8-4-4-4-12
    */
    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rd;                                   // 可以生成一个机器随机数，但效率低
            std::mt19937_64 generator(rd());                         // 通过梅森算法，生成一个伪随机数，rd()生成的随机数给它初始化
            std::uniform_int_distribution<int> distribution(0, 255); // 我们要生成的随机数是在0-255之间的
            std::stringstream ss;
            // 生成8个两位的16进制随机数，组成uuid的前16位，格式为8-4-4
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
                if (i == 3 || i == 5 || i == 7)
                {
                    ss << "-";
                }
            }
            static std::atomic<size_t> seq(1); // 全局的原子类型整数，初始化为1,再用它来累加，组成uuid后16位
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
                if (i == 6)
                {
                    ss << "-";
                }
            }
            return ss.str();
        }
    };
    /*
        一个操作文件的类
    */
    class FileHelper
    {
    public:
        FileHelper(const std::string filename) : _filename(filename) {}
        bool exist()
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0;
        }
        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
                return 0;
            return st.st_size;
        }
        bool read(char *buf, size_t offset, size_t len)
        {
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                LOG("%s: 文件打开失败！", _filename.c_str());
                return false;
            }
            ifs.seekg(offset, std::ios::beg); // 跳转到offset位置
            ifs.read(buf, len);               // 读文件
            if (ifs.good() == false)
            {
                LOG("%s: 文件读取失败！", _filename.c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        bool read(std::string &buf)
        {
            size_t fsize = this->size();
            buf.resize(fsize);
            return read(&buf[0], 0, fsize);
        }
        bool write(const char *buf, size_t offset, size_t len)
        {
            std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
            if (fs.is_open() == false)
            {
                LOG("%s: 文件打开失败！", _filename.c_str());
                return false;
            }
            fs.seekg(offset, std::ios::beg);
            fs.write(buf, len);
            if (fs.good() == false)
            {
                LOG("%s: 文件读取失败！", _filename.c_str());
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }
        bool write(const std::string &buf)
        {
            return write(buf.c_str(), 0, buf.size());
        }
        static std::string getParentDirectory(const std::string &filename)
        {
            size_t pos = filename.find_last_of('/');
            if (pos == std::string::npos)
            {
                return "./";
            }
            std::string path = filename.substr(0, pos);
            return path;
        }
        bool rename(const std::string &newname)
        {
            return ::rename(_filename.c_str(), newname.c_str()) == 0;
        }
        static bool createFile(const std::string &filename)
        {
            std::fstream fs(filename, std::ios::binary | std::ios::out);
            if (fs.is_open() == false)
            {
                LOG("%s: 文件打开失败！", filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        static bool removeFile(const std::string &filename)
        {
            return ::remove(filename.c_str()) == 0;
        }
        static bool createDirectory(const std::string &path)
        {
            // 在多级路径创建中，需要从第一个父级目录开始创建，如aaa/bbb/ccc需要从aaa开始检查是否存在
            size_t pos, idx = 0;
            while (idx < path.size())
            {
                pos = path.find("/", idx);
                // 最后一级目录的创建
                if (pos == std::string::npos)
                {
                    return mkdir(path.c_str(), 0775) == 0;
                }
                // 检查父级目录，有则通过errno判断跳过，无则创建
                std::string subpath = path.substr(0, pos);
                int ret = mkdir(subpath.c_str(), 0775);
                if (ret != 0 && errno != EEXIST)
                {
                    LOG("%s:创建目录失败！", subpath.c_str());
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }
        static bool removeDirectory(const std::string &path)
        {
            std::string cmd = "rm -rf " + path;
            return system(cmd.c_str()) == 0;
        }

    private:
        std::string _filename;
    };
}

#endif